package mybatis.code.generate;

import org.mybatis.generator.api.GeneratedJavaFile;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

public class MapperPlugin extends PluginAdapter {

    private String daoTargetPackage;
    private String servicePackage;
    private String targetDir;

    @Override
    public boolean validate(List<String> warnings) {

        targetDir = properties.getProperty("targetProject");
        boolean valid1 = stringHasValue(targetDir);

        daoTargetPackage = properties.getProperty("targetPackage");
        boolean valid2 = stringHasValue(daoTargetPackage);

        servicePackage = properties.getProperty("servicePackage");

        return valid1 && valid2;
    }

    @Override
    public List<GeneratedJavaFile> contextGenerateAdditionalJavaFiles(IntrospectedTable introspectedTable) {
        //获取所有生成的文件
        List<GeneratedJavaFile> generatedJavaFiles = introspectedTable.getGeneratedJavaFiles();
        for (GeneratedJavaFile javaFile : generatedJavaFiles) {
            //获取编辑项(存放了生成文件时所需要的信息)
            CompilationUnit unit = javaFile.getCompilationUnit();
            FullyQualifiedJavaType baseModelJavaType = unit.getType();
            String shortName = baseModelJavaType.getShortName();
            //判断生成的文件是否是Mapper接口
            if (shortName.endsWith("Mapper")) {
                if (stringHasValue(daoTargetPackage)) {
                    if (unit instanceof Interface) {
                        addSelectAllMethod(unit, introspectedTable);
                    }
                }
            } else if (shortName.endsWith("Entity")) {
                addFieldAnnotation(unit, introspectedTable);
                modifyShort2Integer(unit, introspectedTable);
                modifyGet2Is(unit, introspectedTable);
            }
        }
        String generateService = introspectedTable.getTableConfigurationProperty("generateService");
        if (!"false".equals(generateService)) {
            generatedJavaFiles.add(generatedServiceFile(introspectedTable));
        }
        return generatedJavaFiles;
    }

    /**
     * 生成service类
     * @param introspectedTable
     * @return
     */
    private GeneratedJavaFile generatedServiceFile(IntrospectedTable introspectedTable) {
        String entityName = introspectedTable.getTableConfiguration().getDomainObjectName();
        String serviceName = entityName.replace("Entity", "Manager");
        TopLevelClass serviceClass = new TopLevelClass(servicePackage + "." + serviceName);
        serviceClass.setVisibility(JavaVisibility.PUBLIC);
        FullyQualifiedJavaType entityClass = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
        serviceClass.addImportedType(entityClass);
        FullyQualifiedJavaType mapperInterface = new FullyQualifiedJavaType(introspectedTable.getMyBatis3JavaMapperType());
        serviceClass.addImportedType(mapperInterface);
        serviceClass.addAnnotation("@Service");
        serviceClass.addAnnotation("@Slf4j");
        serviceClass.addImportedType(new FullyQualifiedJavaType("org.springframework.stereotype.Service"));
        serviceClass.addImportedType(new FullyQualifiedJavaType("javax.annotation.Resource"));
        serviceClass.addImportedType(new FullyQualifiedJavaType("lombok.extern.slf4j.Slf4j"));

        // 增加mapper变量
        String mapperFieldName = StringUtils.uncapitalize(mapperInterface.getShortName());
        Field mapperField = new Field(mapperFieldName, mapperInterface);
        mapperField.addAnnotation("@Resource");
        mapperField.setVisibility(JavaVisibility.PRIVATE);
        serviceClass.addField(mapperField);

        // 增加insert方法
        Method method = new Method("insert" + entityName.replace("Entity", ""));
        method.setVisibility(JavaVisibility.PUBLIC);
        Parameter parameter = new Parameter(entityClass, "entity");
        method.addParameter(parameter);
        method.addBodyLine(mapperFieldName + ".insertSelective(entity);");
        serviceClass.addMethod(method);
        GeneratedJavaFile generatedJavaFile = new GeneratedJavaFile(serviceClass, targetDir, context.getJavaFormatter());
        return generatedJavaFile;
    }

    private void addSelectAllMethod(CompilationUnit unit, IntrospectedTable introspectedTable) {
        /*为文件添加扩展的方法*/
        //添加import项
        unit.addImportedType(new FullyQualifiedJavaType("java.util.List"));
        unit.addImportedType(new FullyQualifiedJavaType("com.hz.pro.kitchen.dal.BasicMapper"));
        unit.addImportedType(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        //新建一个方法
        Method method = new Method();
        //设置访问权限
        method.setVisibility(JavaVisibility.PUBLIC);
        //设置方法名称
        method.setName("selectAll");
        //设置返回类型
        FullyQualifiedJavaType returnFullyQualifiedJavaType = new FullyQualifiedJavaType(String.format("List<%s>", introspectedTable.getTableConfiguration().getDomainObjectName()));
        method.setReturnType(returnFullyQualifiedJavaType);
        //设置注解
        AnnotatedSelectAllMethodGengrator annotatedSelectAllMethodGengrator = new AnnotatedSelectAllMethodGengrator(introspectedTable, false, false);
        annotatedSelectAllMethodGengrator.addMapperAnnotations(((Interface) unit), method);
        //把方法添加到编辑项
        ((Interface) unit).addMethod(method);
        Iterator<FullyQualifiedJavaType> iterator = ((Interface) unit).getSuperInterfaceTypes().iterator();
        while (iterator.hasNext()) {
            iterator.next().addTypeArgument(new FullyQualifiedJavaType(introspectedTable.getTableConfiguration().getDomainObjectName()));
        }
    }

    private void addFieldAnnotation(CompilationUnit unit, IntrospectedTable introspectedTable) {
        Map<String, Field> map = new HashMap<>();
        if (unit instanceof TopLevelClass) {
            ((TopLevelClass) unit).addAnnotation("@Table(name = \"" + introspectedTable.getTableConfiguration().getTableName() + "\")");
            unit.addImportedType(new FullyQualifiedJavaType("javax.persistence.Table"));
            List<Field> fieldList = ((TopLevelClass) unit).getFields();
            for (Field field : fieldList) {
                map.put(field.getName(), field);
            }
        }
        boolean hasAnnotation = false;
        List<IntrospectedColumn> introspectedColumns = introspectedTable.getAllColumns();
        for (IntrospectedColumn introspectedColumn : introspectedColumns) {
            if (!introspectedColumn.getJavaProperty().equals(introspectedColumn.getActualColumnName())) {
                Field field = map.get(introspectedColumn.getJavaProperty());
                field.addAnnotation("@Column(name = \"" + introspectedColumn.getActualColumnName() + "\")");
                hasAnnotation = true;
            }
        }
        if (hasAnnotation) {
            unit.addImportedType(new FullyQualifiedJavaType("javax.persistence.Column"));
        }
    }

    private void modifyShort2Integer(CompilationUnit unit, IntrospectedTable introspectedTable) {
        if (unit instanceof TopLevelClass) {
            List<Field> fieldList = ((TopLevelClass) unit).getFields();
            for (Field field : fieldList) {
                if (needChange2Integer(field.getType())) {
                    field.setType(getIntegerType());
                }
            }
            List<Method> methodList = ((TopLevelClass) unit).getMethods();
            for (Method method : methodList) {
                if (method.getReturnType() != null) {
                    if (needChange2Integer(method.getReturnType())) {
                        method.setReturnType(getIntegerType());
                    }
                }
                List<Parameter> parameterList = method.getParameters();
                if (parameterList != null && parameterList.size() == 1) {
                    Parameter parameter = parameterList.get(0);
                    if (needChange2Integer(parameter.getType())) {
                        Parameter newParam = new Parameter(getIntegerType(), parameter.getName());
                        parameterList.add(newParam);
                        parameterList.remove(parameter);
                    }
                }
            }
        }
    }

    private boolean needChange2Integer(FullyQualifiedJavaType type) {
        String typeName = type.getShortName();
        return "Short".equals(typeName) || "Byte".equals(typeName);
    }

    private FullyQualifiedJavaType getIntegerType() {
        return new FullyQualifiedJavaType("java.lang.Integer");
    }

    private void modifyGet2Is(CompilationUnit unit, IntrospectedTable introspectedTable) {
        if (unit instanceof TopLevelClass) {
            List<Method> methodList = ((TopLevelClass) unit).getMethods();
            for (Method method : methodList) {
                if (method.getReturnType() != null) {
                    if ("Boolean".endsWith(method.getReturnType().getShortName())) {
                        method.setName(method.getName().replace("get", "is"));
                    }
                }
            }
        }
    }
}
